package com.dimao.certificate_build_service.pdf.kotiln

import com.dimao.certificate_build_service.pdf.Assignable
import com.dimao.certificate_build_service.pdf.PDFConfig
import com.itextpdf.forms.PdfAcroForm
import com.itextpdf.io.font.PdfEncodings
import com.itextpdf.kernel.font.PdfFont
import com.itextpdf.kernel.font.PdfFontFactory
import com.itextpdf.kernel.pdf.PdfDocument
import com.itextpdf.kernel.pdf.PdfReader
import com.itextpdf.kernel.pdf.PdfWriter
import io.vertx.core.Handler
import io.vertx.core.Vertx
import io.vertx.core.buffer.Buffer
import io.vertx.core.json.JsonArray
import io.vertx.core.json.JsonObject
import io.vertx.kotlin.coroutines.awaitResult
import kotlinx.coroutines.*
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
import java.io.IOException

class PDFBuilder(private val assignable: Assignable,
                 private val basePdfName: String,
                 private val fontOneName: String,
                 private val fontTwoName: String,
                 private val fontThreeName: String) {
    companion object {
        private lateinit var vertx: Vertx
        lateinit var config: PDFConfig

        @JvmStatic
        fun init(v: Vertx, c: PDFConfig) {
            vertx = v
            config = c
        }

        @JvmStatic
        fun getBuilder(assignable: Assignable, basePdfName: String) : PDFBuilder {
            return PDFBuilder(assignable, basePdfName, "one", "two", "three")
        }
    }

    suspend fun build(buildInfo: JsonObject): Buffer = coroutineScope {
        val basePdfFuture = async { awaitResult<Buffer> { h->
            vertx.fileSystem().readFile("${config.basePDFPath}/$basePdfName.pdf", h)
        } }

        val fontOneFuture = async { awaitResult<PdfFont> { h->
            vertx.executeBlocking<PdfFont>(Handler { promise->
                try { promise.complete(
                        PdfFontFactory.createFont(
                                "${config.fontPath}/$fontOneName.ttf",
                                PdfEncodings.IDENTITY_H,
                                true
                        )
                ) } catch (e: IOException){ promise.fail(e) }
            }, h)
        } }

        val fontTwoFuture = async { awaitResult<PdfFont> { h->
            vertx.executeBlocking<PdfFont>(Handler { promise->
                try { promise.complete(
                        PdfFontFactory.createFont(
                                "${config.fontPath}/$fontTwoName.ttf",
                                PdfEncodings.IDENTITY_H,
                                true
                        )
                ) } catch (e: IOException){ promise.fail(e) }
            }, h)
        } }

        val fontThreeFuture = async { awaitResult<PdfFont> { h->
            vertx.executeBlocking<PdfFont>(Handler { promise->
                try { promise.complete(
                        PdfFontFactory.createFont(
                                "${config.fontPath}/$fontThreeName.ttf",
                                PdfEncodings.IDENTITY_H,
                                true
                        )
                ) } catch (e: IOException){ promise.fail(e) }
            }, h)
        } }

        val basePdf = basePdfFuture.await()
        val fonts = arrayListOf(fontOneFuture.await(), fontTwoFuture.await(), fontThreeFuture.await())

        return@coroutineScope awaitResult<Buffer> { h->
            vertx.executeBlocking(Handler { promise->
                var inputStream: ByteArrayInputStream? = null
                var outputStream: ByteArrayOutputStream? = null
                var pdfDocument: PdfDocument? = null

                try {
                    inputStream = ByteArrayInputStream(basePdf.bytes)
                    outputStream = ByteArrayOutputStream()
                    pdfDocument = PdfDocument(PdfReader(inputStream), PdfWriter(outputStream))
                    val form = PdfAcroForm.getAcroForm(pdfDocument, true)
                    assignable.setValue(form.formFields, fonts, buildInfo)
                    form.flattenFields()
                } catch (e: IOException){
                    promise.fail(e)
                } finally {
                    pdfDocument?.close()
                    outputStream?.close()
                    inputStream?.close()
                }
                promise.complete(Buffer.buffer(outputStream?.toByteArray()))
            }, h)
        }
    }
}